#include<iostream>
#include <type_traits>
#include "MPCLinearPrograms.h"
#include <vector>

mpclp::MPCLinearProgram::MPCLinearProgram(std::string filePath)
{
	readFromFile(filePath.c_str(),objective,&constr_matr_P,&constr_matr_C,&constr_right_p,&constr_right_c,&price_vec,num_var,num_constr_pack,num_constr_cov,num_nonzero_elements_pack,num_nonzero_elements_cov);
	status=0;
	num_constr_pack_reduced=num_constr_pack;
	num_var_reduced = num_var;
	num_nonzero_elements_pack_reduced = num_nonzero_elements_pack;
	try
	{
		if(num_var<0)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: Number of variables is not properly initialized. EXIT.";
			std::cout<<std::endl;
		}
		opt_var = new real[num_var];
	}
	catch(std::bad_alloc&)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: Vector of packing variables can not be allocated. Memory Allocation failed. EXIT.";
		std::cout<<std::endl;
	}

	for(unsigned int j=0; j<num_var; j++)
	{
		opt_var[j]=-1;
	}

}



void mpclp::MPCLinearProgram::readFromFile(std::string filePath, int &objective,matrix_type** P, matrix_type**C, real **p, real **c, real **w, unsigned int &n,unsigned int &m, unsigned int &k, unsigned long &nonZerosPack, unsigned long &nonZerosCov)/*C - function*/
{
	//opening file
	std::ifstream inputFile;
	inputFile.open(filePath.c_str(),std::ifstream::in);

	if(!inputFile.is_open())
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: File: "<<filePath<<" can not be opened. EXIT.";
		std::cout<<std::endl;
		exit(0);
	}
	int obj;
	unsigned int num_constr_pack;
	unsigned int num_var_pack;
	unsigned int num_nonzero_pack;

	unsigned int num_constr_cov;
	unsigned int num_var_cov;
	unsigned int num_nonzero_cov;
	inputFile>>obj;
	inputFile>> num_constr_pack >> num_var_pack >> num_nonzero_pack;
	inputFile>> num_constr_cov >> num_var_cov >> num_nonzero_cov;




	if(obj!=-1&&obj!=1)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: WRONG file format. It can not be recognized whether a minimization or a maximization problem is considered. Only two values are permitted: 1 - minimization; -1 - maximization. Given value = "<<obj<<" EXIT"<<std::endl;
		std::cout<<std::endl;
		exit(0);
	}
	if(num_constr_pack==0)
	{
		std::cout<<"STATUS: An instance file reports pure Covering Linear Programming Problem (minimization)."<<std::endl;
	}
	if(num_var_pack==0)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: An instance file reports zero variables. EXIT"<<std::endl;
		std::cout<<std::endl;
		exit(0);
	}
	if(num_nonzero_pack==0)
	{
		std::cout<<std::endl;
		std::cout<<"STATUS: An instance file reports ZERO  packing constraint matrix."<<std::endl;
		std::cout<<std::endl;
	}

	if(num_constr_cov==0)
	{
		std::cout<<std::endl;
		std::cout<<"STATUS: An instance file reports pure Packing Linear Programming Problem (maximization)."<<std::endl;
		std::cout<<std::endl;
	}
	if(num_var_cov==0)
	{
		std::cout<<"ERROR: An instance file reports zero variables. EXIT"<<std::endl;
		exit(0);
	}
	if(num_nonzero_cov==0)
	{
		std::cout<<"STATUS: An instance file reports ZERO  covering constraint matrix."<<std::endl;
	}

	if(num_var_pack!=num_var_cov)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: WRONG file format. Number of columns in the packing and covering constraint matrix has to be equal. EXIT"<<std::endl;
		std::cout<<std::endl;
		exit(0);
	}
	std::cout<<"Number of variables                  = "<<num_var_pack<<std::endl;
	std::cout<<"Number of packing constraints        = "<<num_constr_pack<<std::endl;
	std::cout<<"Number of nonzero elements P matrix  = "<<num_nonzero_pack<<std::endl;
	std::cout<<"Number of covering constraints       = "<<num_constr_cov<<std::endl;
	std::cout<<"Number of nonzero elements C matrix  = "<<num_nonzero_cov<<std::endl;

	m=num_constr_pack;
	n=num_var_pack;
	k=num_constr_cov;
	nonZerosPack=num_nonzero_pack;
	nonZerosCov=num_nonzero_cov;

	// memory alocation for arrays
	*P = NULL;
	*C = NULL;
	*p = NULL;
	*c = NULL;
	*w = NULL;


	if(num_constr_pack>0 && num_nonzero_pack>0)
	{
		*P = new matrix_type[m*n] ();
		*p = new real[m];
	}
	if(num_constr_cov>0 && num_nonzero_cov>0)
	{
		*C = new matrix_type[k*n] ();
		*c = new real[k];
	}
	*w = new real[n];


	unsigned int row = 0,col = 0;
	matrix_type value;

	for (unsigned int i = 0; i<num_nonzero_pack; i++)
	{
		inputFile>>row>>col>>value;
		if(row>=num_nonzero_pack||col>=num_var_pack)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: WRONG file format. Input instance reports indices out of range. EXIT"<<std::endl;
			std::cout<<row<<" "<<col<<" "<<value<<std::endl;
			std::cout<<std::endl;
			exit(0);

		}
		if(value<0)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: WRONG file format. Entries in packing constraint matrix has to be nonnegative. EXIT"<<std::endl;
			std::cout<<std::endl;
			exit(0);
		}
		(*P)[row*num_var_pack+col]=value;
	}

	for (unsigned int i = 0; i<num_nonzero_cov; i++)
	{
		inputFile>>row>>col>>value;
		if(row>=num_nonzero_cov||col>=num_var_cov)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: WRONG file format. Input instance reports indices out of range. EXIT"<<std::endl;
			std::cout<<row<<" "<<col<<" "<<value<<std::endl;
			std::cout<<std::endl;
			exit(0);

		}
		if(value<0)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: WRONG file format. Entries in covering constraint matrix has to be nonnegative. EXIT"<<std::endl;
			std::cout<<std::endl;
			exit(0);
		}
		(*C)[row*num_var_cov+col]=value;
	}


	real entry;
	for(unsigned int i=0; i<num_constr_pack; i++)
	{
		inputFile>>entry;
		if(entry<0)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: WRONG file format. Entries in right-hand side packing constraint vector has to be nonnegative. EXIT"<<std::endl;
			std::cout<<std::endl;
			exit(0);
		}
		(*p)[i]=entry;
	}
	for(unsigned int j=0; j<num_constr_cov; j++)
	{
		inputFile>>entry;
		if(entry<0)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: WRONG file format. Entries in right-hand side packing constraint vector has to be nonnegative. EXIT"<<std::endl;
			std::cout<<std::endl;
			exit(0);
		}
		(*c)[j]=entry;
	}
	for(unsigned int j=0; j<num_var_pack; j++)
	{
		inputFile>>entry;
		if(entry<0)
		{
			std::cout<<std::endl;
			std::cout<<"ERROR: WRONG file format. Entries in price packing vector has to be nonnegative. EXIT"<<std::endl;
			std::cout<<std::endl;
			exit(0);
		}
		(*w)[j]=entry;
	}

	inputFile.close();
}


bool mpclp::MPCLinearProgram::purePackingPreproccessModel()
{
	//row pre-processing
	for(unsigned int i=0; i<num_constr_pack; i++)
	{
		bool zero_row = true;
		bool zero_constr_right = (constr_right_p[i]==0);
		for(unsigned int j=0; j<num_var; j++)
		{
			if(constr_matr_P[i*num_var+j]>0)
			{
				zero_row=false;
				if(zero_constr_right)
				{
					opt_var[j]=0;
				}
			}
		}
		if(!zero_row&&!zero_constr_right)
		{
			constr_right_p_in.push_back(i);
		}
	}
	//column pre-processing
	for(unsigned int j=0; j<num_var; j++)
	{
		bool zero_column=true;
		bool zero_price_vec = (price_vec[j]==0);
		for(unsigned int i=0; i<num_constr_pack; i++)
		{
			if(constr_matr_P[i*num_var+j]>0)
			{
				zero_column=false;
				break;
			}
		}
		if(zero_price_vec)
		{
			if(zero_column)
			{
				std::cout<<std::endl;
				std::cout<<"WARNING: The value of a "<<j<<"-th packing variable doesn't affect on feasibility and optimality of the solution. Thus, it will be set to 0.";
				std::cout<<std::endl;
				opt_var[j]=0;
			}
			else
			{
				opt_var[j]=0;
			}

		}
		else
		{
			if(zero_column)
			{
				std::cout<<std::endl;
				std::cout<<"WARNING: Packing Linear Programming Model is unbounded. "<<j<<"-th variable can be arbitrarily large."<<std::endl;
				std::cout<<"         "<<j<<"-th variable will be set to INFTY while other variables will be set to zero. EXIT."<<std::endl;
				std::cout<<std::endl;
				for(unsigned int k=0; k<num_var; k++)
				{
					if(k==j)
					{
						opt_var[k]=INFTY;
					}
					else
					{
						opt_var[k]=0;
					}
				}
				status=3;
				exit(0);
			}
		}
		if (opt_var[j]!=0)
		{
			price_vec_in.push_back(j);
		}
	}
	/*std::cout<<std::endl;
	for(unsigned int i=0; i<constr_right_p_in.size(); i++)
	{
		std::cout<<"\t"<<constr_right_p_in.at(i);
	}
	std::cout<<std::endl;
	for(unsigned int j=0; j<price_vec_in.size(); j++)
	{
		std::cout<<"\t"<<price_vec_in.at(j);
	}
	std::cout<<std::endl;*/

	num_var_reduced=price_vec_in.size();
	num_constr_pack_reduced=constr_right_p_in.size();

	std::cout<<"num_var_pack_reduced    = "<<num_var_reduced<<std::endl;
	std::cout<<"num_constr_pack_reduced = "<<num_constr_pack_reduced<<std::endl;

	if(num_var_reduced==num_var && num_constr_pack_reduced==num_constr_pack)
	{
		std::cout<<std::endl;
		std::cout<<"STATUS: Packing Linear Programming Model is preproccessed. There is no need for deleting of variables nor constraints.";
		std::cout<<std::endl;
		status=1;
		return true;

	}
	for(unsigned int i=0; i<num_constr_pack_reduced; i++)
	{
		for(unsigned int j=0; j<num_var_reduced; j++)
		{
				constr_matr_P[i*num_var+j]=constr_matr_P[constr_right_p_in.at(i)*num_var+price_vec_in.at(j)];
		}
	}

	for(unsigned int i=0; i<num_constr_pack_reduced; i++)
	{
			constr_right_p[i]=constr_right_p[constr_right_p_in.at(i)];
	}
	for(unsigned int j=0; j<num_var_reduced; j++)
	{
			price_vec[j]=price_vec[price_vec_in.at(j)];
	}
	status=1;
	return true;
}

bool mpclp::MPCLinearProgram::pureCoveringPreproccessModel()
{
	//row pre-processing
	for(unsigned int i; i<num_constr_cov; i++)
	{
		bool zero_row = true;
		bool zero_constr_right = (constr_right_c[i]==0);
		for(unsigned int j=0; j<num_var; j++)
		{

		}

	}



	return true;
}

void mpclp::PackingLinearProgram::normalizeModel()
{
	if(status==1)
	{
		for(unsigned int i=0; i<num_constr_pack_reduced; i++)
		{
			for(unsigned int j=0; j<num_var_pack_reduced; j++)
			{
				constr_matr_P[i*num_var_pack+j]/=constr_right_p[i]*price_vec[j];
			}
		}

	}
	else if (status==0)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: Model hasn't been preprocessed yet. EXIT";
		std::cout<<std::endl;
		exit(0);
	}
	else
	{
		std::cout<<std::endl;
		std::cout<<"STATUS: Model is already normalized.";
		std::cout<<std::endl;
	}
	return;
}




real mpclp::PackingLinearProgram::densityInPercent()
{
	if(num_var_pack>0 && num_constr_pack>0)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: Number of variables and number of packing constraints are not properly initialized. EXIT.";
		std::cout<<std::endl;
		exit(0);
	}
	return num_nonzero_elements_pack/(num_var_pack*num_constr_pack);
}

real mpclp::PackingLinearProgram::optimalValue()
{
	if(status!=3)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: Packing Linear Programming Model is not solved. Optimal value of the objective function can't be returned. EXIT.";
		std::cout<<std::endl;
		exit(0);
	}
	if(num_var_pack<=0)
	{
		std::cout<<std::endl;
		std::cout<<"ERROR: Packing Linear Programming Model is not properly initialized. Number of packing variables must be a positive integer. Optimal value of the objective function can't be returned. EXIT.";
		std::cout<<std::endl;
		exit(0);
	}
	real opt_value=0;
	for(unsigned int i=0; i<num_var_pack; i++)
	{
		opt_value+=price_vec[i]*opt_var[i];
	}
	return opt_value;
}

mpclp::PackingLinearProgram::~PackingLinearProgram()
{
	delete [] constr_matr_P;
	delete [] price_vec;
	delete [] constr_right_p;
}
